package com.demo.ui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;

import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.filechooser.FileSystemView;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellEditor;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import com.demo.data.FileNode;
import com.demo.utils.Logger;

public class TreeView extends JPanel
		implements TreeExpansionListener, TreeSelectionListener, MouseListener, ActionListener, TreeWillExpandListener {

	private static final long serialVersionUID = -1390144896501901715L;

	private DefaultTreeModel mTreeMode;
	private JTree mTree;
	private MutableTreeNode mTreeRoot;

	private JPopupMenu mPopupFileMenu;
	private JPopupMenu mPopupDirMenu;
	private JMenuItem mNewFileItem;
	private JMenuItem mNewDirItem;
	private JMenuItem mRenameItem;
	private JMenuItem mDeleteItem;
	private JMenuItem mRefreshItem;

	private FileSystemView mFileSystemView;

	private OnTreeNodeListener mTreeNodeListener;
	private OnTreeClickListener mTreeClickListener;

	public TreeView(FileNode root) {
		mFileSystemView = FileSystemView.getFileSystemView();
		this.setLayout(new BorderLayout());
		mTreeRoot = new DefaultMutableTreeNode(root);
		mTreeMode = new MyTreeModel(mTreeRoot);
		mTree = new JTree(mTreeMode);
		mTree.setEditable(true);
		mTree.setRootVisible(false);
		mTree.setCellRenderer(new MyTreeRenderer());
		mTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		mTree.setCellEditor(new DefaultTreeCellEditor(mTree, new DefaultTreeCellRenderer()));
		mTree.addMouseListener(this);
		mTree.addTreeSelectionListener(this);
		mTree.addTreeExpansionListener(this);
		mTree.addTreeWillExpandListener(this);
		add(new JScrollPane(mTree), BorderLayout.CENTER);

		mNewFileItem = new JMenuItem("New File");
		mNewFileItem.addActionListener(this);
		mNewDirItem = new JMenuItem("New Director");
		mNewDirItem.addActionListener(this);
		mRenameItem = new JMenuItem("Rename");
		mRenameItem.addActionListener(this);
		mDeleteItem = new JMenuItem("Delete");
		mDeleteItem.addActionListener(this);
		mRefreshItem = new JMenuItem("Refresh");
		mRefreshItem.addActionListener(this);

		mPopupFileMenu = new JPopupMenu();
		mPopupFileMenu.add(mRenameItem);
		mPopupFileMenu.add(mDeleteItem);

		mPopupDirMenu = new JPopupMenu();
		mPopupDirMenu.add(mNewFileItem);
		mPopupDirMenu.add(mNewDirItem);
		mPopupDirMenu.add(mRenameItem);
		mPopupDirMenu.add(mDeleteItem);
		mPopupDirMenu.add(mRefreshItem);
	}

	public void add(MutableTreeNode parent, FileNode project) {
		add(parent, project, true);
	}

	public void add(MutableTreeNode parent, FileNode project, boolean hasChild) {
		DefaultMutableTreeNode child = new DefaultMutableTreeNode(project, hasChild);
		mTreeMode.insertNodeInto(child, parent, parent.getChildCount());
	}

	public void remove(MutableTreeNode node) {
		mTreeMode.removeNodeFromParent(node);
	}

	public MutableTreeNode getParentTreeNode() {
		MutableTreeNode parent = (DefaultMutableTreeNode) mTree.getLastSelectedPathComponent();
		return parent == null ? mTreeRoot : parent;
	}

	public DefaultTreeModel getTreeModel() {
		return mTreeMode;
	}

	public JTree getTree() {
		return mTree;
	}

	@Override
	public void treeExpanded(TreeExpansionEvent event) {
		Logger.d("treeExpanded");
	}

	@Override
	public void treeCollapsed(TreeExpansionEvent event) {
		Logger.d("treeCollapsed");
	}

	@Override
	public void valueChanged(TreeSelectionEvent e) {
		Logger.d("valueChanged");
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		Logger.d("mouseClicked");
		if (mTreeRoot.getChildCount() == 0) {
			return;
		}
		if (e.getClickCount() >= 2) {
			Logger.d("double click");
			if (mTreeClickListener != null) {
				mTreeClickListener.onDoubleClick(e);
			}
		} else {
			Logger.d("single click");
			if (mTreeClickListener != null) {
				mTreeClickListener.onSingleClick(e);
			}
		}
	}

	@Override
	public void mousePressed(MouseEvent e) {
		Logger.d("mousePressed");
		TreePath path = mTree.getClosestPathForLocation(e.getX(), e.getY());
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) mTree.getLastSelectedPathComponent();
		if (path == null || node == null) {
			return;
		}
		mTree.setSelectionPath(path);

		if (e.getButton() == 3) {
			if (node.getAllowsChildren()) {
				mPopupDirMenu.show(mTree, e.getX(), e.getY());
			} else {
				mPopupFileMenu.show(mTree, e.getX(), e.getY());
			}
		}
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		Logger.d("mouseReleased");
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		Logger.d("mouseEntered");
	}

	@Override
	public void mouseExited(MouseEvent e) {
		Logger.d("mouseExited");
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		Logger.d("actionPerformed");
		DefaultMutableTreeNode parent = (DefaultMutableTreeNode) mTree.getLastSelectedPathComponent();
		if (e.getSource() == mNewFileItem) {
			String name = JOptionPane.showInputDialog(null, "New a file", "Info", JOptionPane.PLAIN_MESSAGE);
			if (name == null || name.isEmpty()) {
				return;
			}
			FileNode pNode = (FileNode) parent.getUserObject();
			String childPath = pNode.getFile().getAbsolutePath() + File.separator + name;
			FileNode node = new FileNode();
			File file = new File(childPath);
			node.setName(name);
			node.setFile(file);
			node.setIcon(mFileSystemView.getSystemIcon(file));
			node.setDummyRoot(false);
			DefaultMutableTreeNode child = new DefaultMutableTreeNode(node, false);
			mTreeMode.insertNodeInto(child, parent, parent.getChildCount());
			if (mTreeNodeListener != null) {
				mTreeNodeListener.onNewFile(node);
			}
		} else if (e.getSource() == mNewDirItem) {
			String name = JOptionPane.showInputDialog(null, "New a director", "Info", JOptionPane.PLAIN_MESSAGE);
			if (name == null || name.isEmpty()) {
				return;
			}
			FileNode pNode = (FileNode) parent.getUserObject();
			String childPath = pNode.getFile().getAbsolutePath() + File.separator + name;
			FileNode node = new FileNode();
			File file = new File(childPath);
			node.setName(name);
			node.setFile(file);
			node.setIcon(mFileSystemView.getSystemIcon(file));
			node.setDummyRoot(false);
			DefaultMutableTreeNode child = new DefaultMutableTreeNode(node);
			mTreeMode.insertNodeInto(child, parent, parent.getChildCount());
			if (mTreeNodeListener != null) {
				mTreeNodeListener.onNewDir(node);
			}
		} else if (e.getSource() == mRenameItem) {
			TreePath path = mTree.getSelectionPath();
			mTree.startEditingAtPath(path);
		} else if (e.getSource() == mDeleteItem) {
			mTreeMode.removeNodeFromParent(parent);
			if (mTreeNodeListener != null) {
				mTreeNodeListener.onDelete((FileNode) parent.getUserObject());
			}
		} else if (e.getSource() == mRefreshItem) {
			mTreeMode.reload();
		}
	}

	public interface OnTreeNodeListener {

		void onNewFile(FileNode node);

		void onNewDir(FileNode node);

		void onRename(FileNode node);

		void onDelete(FileNode node);
	}

	public static class SimpleTreeNodeListener implements OnTreeNodeListener {

		@Override
		public void onNewFile(FileNode node) {

		}

		@Override
		public void onNewDir(FileNode node) {

		}

		@Override
		public void onRename(FileNode node) {

		}

		@Override
		public void onDelete(FileNode node) {

		}

	}

	public void setOnTreeNodeLister(OnTreeNodeListener listener) {
		this.mTreeNodeListener = listener;
	}

	public interface OnTreeClickListener {

		void onSingleClick(MouseEvent e);

		void onDoubleClick(MouseEvent e);
	}

	public static class SimpleTreeClickListener implements OnTreeClickListener {

		@Override
		public void onSingleClick(MouseEvent e) {

		}

		@Override
		public void onDoubleClick(MouseEvent e) {

		}

	}

	public void setOnTreeClickListener(OnTreeClickListener listener) {
		this.mTreeClickListener = listener;
	}

	private class MyTreeModel extends DefaultTreeModel {

		private static final long serialVersionUID = 6585109468714252148L;

		public MyTreeModel(TreeNode root) {
			super(root);
			// FileSystemView fileSystemView = FileSystemView.getFileSystemView();
			// File[] files = fileSystemView.getRoots();
			File[] files = ((FileNode) (((DefaultMutableTreeNode) root).getUserObject())).getFile().listFiles();
			for (int i = 0; i < files.length; i++) {
				FileNode childFileNode = new FileNode();
				childFileNode.setName(mFileSystemView.getSystemDisplayName(files[i]));
				childFileNode.setIcon(mFileSystemView.getSystemIcon(files[i]));
				childFileNode.setFile(files[i]);
				childFileNode.setDummyRoot(false);
				DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(childFileNode);
				((DefaultMutableTreeNode) root).add(childTreeNode);
			}
		}

		@Override
		public void valueForPathChanged(TreePath path, Object newValue) {
			Object obj = ((DefaultMutableTreeNode) (path.getLastPathComponent())).getUserObject();
			((FileNode) obj).setName(newValue.toString());
			super.valueForPathChanged(path, obj);
		}

		@Override
		public boolean isLeaf(Object node) {
			DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
			FileNode fileNode = (FileNode) treeNode.getUserObject();
			if (fileNode.isDummyRoot())
				return false;
			return fileNode.getFile().isFile();
		}

	}

	private class MyTreeRenderer extends DefaultTreeCellRenderer {

		private static final long serialVersionUID = -1437476781470449898L;

		@Override
		public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
				boolean leaf, int row, boolean hasFocus) {
			JLabel label = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
			DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
			FileNode fileNode = (FileNode) node.getUserObject();
			label.setText(fileNode.getName());
			label.setIcon(fileNode.getIcon());
			label.setOpaque(false);
			return label;
		}

	}

	@Override
	public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
		Logger.d("treeWillExpand");
		DefaultMutableTreeNode lastTreeNode = (DefaultMutableTreeNode) event.getPath().getLastPathComponent();
		FileNode fileNode = (FileNode) lastTreeNode.getUserObject();
		if (!fileNode.isInit()) {
			File[] files;
			if (fileNode.isDummyRoot()) {
				files = mFileSystemView.getRoots();
			} else {
				files = mFileSystemView.getFiles(((FileNode) lastTreeNode.getUserObject()).getFile(), false);
			}
			for (int i = 0; i < files.length; i++) {
				FileNode childFileNode = new FileNode();
				childFileNode.setName(mFileSystemView.getSystemDisplayName(files[i]));
				childFileNode.setIcon(mFileSystemView.getSystemIcon(files[i]));
				childFileNode.setFile(files[i]);
				childFileNode.setDummyRoot(false);
				DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(childFileNode);
				lastTreeNode.add(childTreeNode);
			}
			mTreeMode.nodeStructureChanged(lastTreeNode);
		}
		fileNode.setInit(true);
	}

	@Override
	public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
		Logger.d("treeWillCollapse");
	}

}
